package ar.com.jsejtman.common.security.schema.entity;

import ar.com.jsejtman.common.domain.entity.Identifiable;
import ar.com.jsejtman.common.domain.entity.IdentifiableComparer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Version;

/**
 * Grupos de autoridades (es decir, grupos de permisos) disponibles en el sistema para uno o más
 * usuarios.
 * 
 * @author jsejtman
 */
@Entity
@Table(name = "security_authorities_group")
public class AuthoritiesGroup implements Identifiable<Long> {

	/**
	 * Identificador del grupo de autoridades.
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;

	/**
	 * Código del grupo de autoridades.
	 */
	@Column(name = "code", nullable = false)
	private String code;

	/**
	 * Descripción del grupo de autoridades.
	 */
	@Column(name = "description", nullable = false)
	private String description;

	/**
	 * Autoridades que comprende el grupo (es decir, todos los permisos del grupo).
	 */
	@ManyToMany
	@JoinTable(name = "authorities_group_authority", 
			joinColumns = { @JoinColumn(name = "authorities_group_id") }, 
			inverseJoinColumns = { @JoinColumn(name = "authority_id") })
	private List<Authority> authorities;

	/**
	 * Número de versión de la entidad (para evitar escrituras concurrentes).
	 */
	@Version
	@Column(name = "version", nullable = false)
	private Long version;

	/**
	 * Devuelve el valor de id.
	 * 
	 * @return El valor de id.
	 */
	public final Long getId() {

		return (this.id);
	}

	/**
	 * Asigna un nuevo valor a id.
	 * 
	 * @param id El valor a asignar a id.
	 */
	protected final void setId(final Long id) {

		this.id = id;
	}

	/**
	 * Devuelve el valor de code.
	 * 
	 * @return El valor de code.
	 */
	public final String getCode() {

		return (this.code);
	}

	/**
	 * Asigna un nuevo valor a code.
	 * 
	 * @param code El valor a asignar a code.
	 */
	public final void setCode(final String code) {

		this.code = code;
	}

	/**
	 * Devuelve el valor de description.
	 * 
	 * @return El valor de description.
	 */
	public final String getDescription() {

		return (this.description);
	}

	/**
	 * Asigna un nuevo valor a description.
	 * 
	 * @param description El valor a asignar a description.
	 */
	public final void setDescription(final String description) {

		this.description = description;
	}

	/**
	 * Devuelve el valor de authorities.
	 * 
	 * @return El valor de authorities.
	 */
	protected final List<Authority> getAuthorities() {

		return (this.authorities);
	}

	/**
	 * Asigna un nuevo valor a authorities.
	 * 
	 * @param authorities El valor a asignar a authorities.
	 */
	protected final void setAuthorities(final List<Authority> authorities) {

		this.authorities = authorities;
	}

	/**
	 * Agrega una nueva autoridad al grupo de autoridades.
	 * 
	 * @param authority Autoridad a agregar.
	 * @return True si la agregó; caso contrario, false.
	 */
	public final boolean addAuthority(final Authority authority) {

		/* Instancia la colección */
		if (this.authorities == null) {
			this.authorities = new ArrayList<Authority>();
		}

		return (this.authorities.add(authority));
	}

	/**
	 * Quita una autoridad de un grupo de autoridades.
	 * 
	 * @param authority Autoridad a quitar.
	 * @return True si la quitó; caso contrario, false.
	 */
	public final boolean removeAuthority(final Authority authority) {

		/*
		 * No puede intentar remover objetos nulos, o cuando la colección aún no ha sido
		 * inicializada.
		 */
		if (authority == null || this.authorities == null) {
			return (false);
		}

		return (this.authorities.remove(authority));
	}

	/**
	 * Obtiene la lista de todas las autoridades.
	 * 
	 * @return Lista de autoridades.
	 */
	public final List<Authority> getAllAuthorities() {

		/*
		 * Si la colección no fue inicializada, devuelve una colección vacía (impide la generación
		 * de punteros nulos).
		 */
		if (this.authorities == null) {
			return (Collections.unmodifiableList(new ArrayList<Authority>()));
		}

		return (Collections.unmodifiableList(this.authorities));
	}

	/**
	 * Devuelve el valor de version.
	 * 
	 * @return El valor de version.
	 */
	protected final Long getVersion() {

		return (this.version);
	}

	/**
	 * Asigna un nuevo valor a version.
	 * 
	 * @param version El valor a asignar a version.
	 */
	protected final void setVersion(final Long version) {

		this.version = version;
	}

	@Override
	public final int hashCode() {

		return ((this.id == null) ? 0 : this.id.hashCode());
	}

	@SuppressWarnings("unchecked")
	@Override
	public final boolean equals(final Object obj) {

		return (IdentifiableComparer.equals(this, (Identifiable<Serializable>) obj));
	}
}
